//
// Written by Motti Shaked
//
// motti@wincode.net
//
// Version: 1.0.0.0
//
// DISCLAIMER:
//
// You may use this code as you please.
// You may modify and share it freely.
// You may NOT hold me liable for any damage
// caused to you, your company, your neighbors,
// or anyone else as a result of using this code,
// taking ideas from it, dreaming of it,
// or any other use you may find it suitable for.
// Whatever you do with this code is at 
// your own risk.
//
// Enjoy!
// 

using System;
using System.Collections;
using System.Runtime.Remoting.Channels;
using System.Reflection;
using Universe.Remoting.Sinks;

namespace Universe.Remoting.Sinks
{
    public abstract class BaseCustomSinkProvider
    {
        protected SinkProviderData data;
        protected Type customSinkType;
        protected object perProviderState;
        protected const string initMethodName = "Init";

        public BaseCustomSinkProvider(IDictionary properties, ICollection providerData)
        {
            // get custom sink type from configuration file
            string customSinkType = (string)properties["customSinkType"];
            if (customSinkType == null)
            {
                throw new CustomSinkException("no customSinkType property in the <provider> element.");
            }
            this.customSinkType = Type.GetType(customSinkType);
            if (this.customSinkType == null)
            {
                throw new CustomSinkException(
                    string.Format("Could not load type {0}", customSinkType));
            }

            // make sure the custom sink type inherits BaseCustomSink
            if (!this.customSinkType.IsSubclassOf(typeof(BaseCustomSink)))
            {
                throw new CustomSinkException("Custom sink type does not inherit from BaseCustomSink");
            }

            // see if there is a <customData> element in the provider data
            // and save it for passing it to the custom sink's constructor
            foreach (SinkProviderData data in providerData)
            {
                if (data.Name == "customData")
                {
                    this.data = data;
                    break;
                }
            }

            // see if the custom sink wants to be statically initialized
            // (if it has the following method)
            // public static void Init(SinkProviderData data, ref object perProviderState)
            Type [] paramTypes = {typeof(SinkProviderData), Type.GetType("System.Object&")};
            MethodInfo initMethod = this.customSinkType.GetMethod(initMethodName, paramTypes);
            if (initMethod != null)
            {
                object [] param = {this.data, null};
                try
                {
                    initMethod.Invoke(null, param);
                    this.perProviderState = param[1];
                }
                catch (TargetInvocationException e)
                {
                    throw new CustomSinkException(
                        string.Format("The static Init method of type {0} threw a {1} exception. Message: {2}", 
                                      this.customSinkType.ToString(), e.InnerException.GetType().ToString(),
                                      e.InnerException.Message),
                        e);
                }
            }

        }

        protected static IDictionary CreateProperties(Type customSinkType)
        {
            Hashtable ret = new Hashtable();
            ret["customSinkType"] = customSinkType.AssemblyQualifiedName;
            return ret;
        }
    }
}